package com.sakila.action.staff;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.sakila.action.BaseAction;
import com.sakila.action.util.JsonUtil;
import com.sakila.jaas.RoleDefinition;
import com.sakila.pojo.Role;
import com.sakila.pojo.Staff;
import com.sakila.pojo.other.Page;
import com.sakila.service.AddressService;
import com.sakila.service.CityService;
import com.sakila.service.StaffService;
import com.sakila.service.StoreService;
import com.sakila.vo.MyUser;

@Controller
@RequestMapping(value={"/staff"})
public class StaffAction implements BaseAction {
	private static final Logger logger = LoggerFactory.getLogger(StaffAction.class);
	@Resource
	private StaffService staffService;
	@Resource
	private StoreService storeService;
	@Resource
	private AddressService addressService;
	@Resource
	private CityService cityService;
	
	//Page jumping
	@RequestMapping(value="/admin/goto_addStaff")
	public String dipatcherAddStaff(Model model, Authentication au){
		Staff staff = ((MyUser)au.getPrincipal()).getStaff();
		this.setModelAndView(model, new Staff(), au);				
		return "staff/admin/addStaff";
	}
	
	@RequestMapping(value="/goto_modifyOwn")
	public String dispatcherModifyOwn(Authentication au, Model model){
		MyUser user = (MyUser) au.getPrincipal();
		model.addAttribute("staff", user.getStaff());
		return "staff/modifyOwn";
	}
	
	@RequestMapping(value="/admin/goto_modifyStaff/{staffId}")
	public String dipatcherModify(Model model, @PathVariable Integer staffId, Authentication au){
		Staff staff = staffService.findById(staffId);
		this.setModelAndView(model, staff, au);
		return "staff/admin/modifyStaff";
	}
	
	@RequestMapping(value="/goto_changePassword")
	public String dispatcherChangePassword(Model model, Authentication au){
		MyUser user = (MyUser)au.getPrincipal();
		model.addAttribute("staff", user.getStaff());
		return "staff/admin/changePwd";
	}
	
	@RequestMapping(value="/goto_changeAvatar")
	public String dispatcherChangeAvator(Model model, Authentication au){
		MyUser user = (MyUser)au.getPrincipal();
		model.addAttribute("staff", user.getStaff());
		return "staff/admin/changeAvatar";
	}
	
	//Data process
	@RequestMapping(value="/admin/viewStaff/{id}")
	public String viewStaff(@PathVariable("id")int id, Model model, Authentication au){
		Staff staff = staffService.findById(id);
		this.setModelAndView(model, staff, au);
		return "staff/staff";
	}
	
	@RequestMapping(value={"/admin/goto_viewStaff"})
	public String viewList() {
		return "staff/admin/index";
	}
	
	@RequestMapping(value={"/admin/viewStaff"})
	@ResponseBody
	public Object viewList(
			@RequestParam(value=PAGE_ID,defaultValue="1")int pageId,
			@RequestParam(value=PAGE_SIZE,defaultValue="30")int pageSize,
			Authentication au) {
		//Page page = new Page(pageId, pageSize);
		MyUser user = (MyUser) au.getPrincipal();
		List<Staff> list = staffService.viewSubStaffs(user.getStaff());	
		return JsonUtil.toJson(list);
	}
	
	@RequestMapping(value="/admin/addStaff", method=RequestMethod.POST)
	public String addStaff(Authentication au, Model model, @Valid Staff staff, Errors errors, 
							@RequestParam MultipartFile upload, HttpServletRequest request){
		logger.debug("Staff:......" + staff.getAddress().getId());
		logger.debug("Store:......" + staff.getStore().getId());
		
		if(staff.getPassword() == null){
			logger.debug(">>>>>>>>>>>>>>>>register member: password is empty, add new error.");
			errors.rejectValue("password", "Not Null", "password should not be empty");
		}
		
		if(errors.hasErrors()){
			/*List<ObjectError> es = errors.getAllErrors();
			for (ObjectError objectError : es) {
				System.out.println(objectError);
			}*/
			this.setModelAndView(model, staff, au);
			return "staff/admin/addStaff";
		}
		
		BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(16);
		String cryptedPwd = passwordEncoder.encode(new StringBuilder(staff.getPassword()));
		staff.setPassword(cryptedPwd);
		
		this.setStaffPicture(staff, upload);
		staffService.saveStaff(staff);
		
		//after saving, mybatis will set the value of the primary key to the object.
		//after that, you can make a picture which named with its primary key value.
		this.saveFileToImagesFolder(staff, upload, request, false);	
		return "redirect:/staff/admin/goto_viewStaff";
	}
	
	@RequestMapping(value="/admin/modifyStaff", method=RequestMethod.POST)
	public String modifyStaff(@Valid Staff staff, Errors errors, Model model, 
							@RequestParam(required=false)Boolean markAsManager, Authentication au){
		if(errors.hasErrors()){
			logger.debug(">>>>>>>>>>>>>>>>>Error occur: modify member.");
			
			//Be careful, the request must contain the actual validated object not creating a new one.
			this.setModelAndView(model, staff, au);
			return "staff/admin/modifyStaff";
		}
		
		if(markAsManager != null && staffService.isManager(staff) == false){
			logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>Set new manager: ");
			staffService.setStoreManager(staff);
			/*if(storeService.findManager(staff.getStore()) == null){
				logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>Check OK!!!!!!!!! ");				
			}else {
				model.addAttribute("error", "exist other manager in the store.");
				this.setModelAndView(model, staff);
				return "staff/admin/modifyStaff";
			}*/
		}
		
		if(staff.getPassword() != null){
			logger.debug(">>>>>>>>>>>>>>>>>Modify member: new password set.");
			BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(16);
			String cryptedPwd = passwordEncoder.encode(new StringBuilder(staff.getPassword()));
			staff.setPassword(cryptedPwd);
		}
		staffService.update(staff);
		return "redirect:/staff/admin/goto_viewStaff";
	}
	
	@RequestMapping(value="/changePassword", method=RequestMethod.POST)
	public String changePassword(Staff staff){
		if(staff.getPassword() != null){
			logger.debug(">>>>>>>>>>>>>>>>>Modify member: new password set.");
			BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(16);
			String cryptedPwd = passwordEncoder.encode(new StringBuilder(staff.getPassword()));
			staff.setPassword(cryptedPwd);
			staffService.update(staff);
			SecurityContextHolder.getContext().setAuthentication(null);
		}
		return "redirect:/staff/";
	}
	
	@RequestMapping(value="/changeAvatar", method=RequestMethod.POST)
	public String changeAvator(Staff staff, @RequestParam MultipartFile upload, HttpServletRequest request){
		//if size is 0, indicate no picture is uploaded. MultipartFile actual is a byte array.
		if(upload.getSize() > 0) {
			this.setStaffPicture(staff, upload);
			staffService.update(staff);
			this.saveFileToImagesFolder(staff, upload, request, true);
		}else{
			staffService.update(staff);
		}
		return "redirect:/staff/";
	}
	
	private void setStaffPicture(Staff staff, MultipartFile upload){
		try {
			staff.setPicture(upload.getBytes());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void setModelAndView(Model model, Staff staff, Authentication au){
		MyUser user = (MyUser)au.getPrincipal();
		if(staffService.isManager(user.getStaff())){
			model.addAttribute("stores", user.getStaff().getStore());
		}else {
			model.addAttribute("stores", storeService.findAll());
		}
		model.addAttribute("staff", staff);	
		model.addAttribute("cities", cityService.findAll());
		
		//Means modify info
		if(staff.getId() != null){
			model.addAttribute("isManager", staffService.isManager(staff));
		}
	}
	
	private void saveFileToImagesFolder(Staff staff, MultipartFile upload, HttpServletRequest request, boolean overwrite){
		String fileName = "staff_" + staff.getId() + ".jpg";
		logger.debug(request.getServletContext().getRealPath("/"));
		
		/**
		 * If set to "/", it will work on local but not on server.
		 * Maybe the right way is setting to the exact path instead of root path.
		 */
		File file = new File(request.getServletContext().getRealPath("/images") + "/" + fileName);
		if((!file.exists() || overwrite) && upload.getSize() > 0){
			try {
				upload.transferTo(file);				
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
